#define MS_CLASS "HttpServer"

#include "HttpServer.h"
#include <string>
#include <random>

#if defined(_WIN32)

#include <WinSock2.h>

#else
#include <sys/time.h>
#endif

#include "Logger.hpp"

// svr.Post("/signaling/", [&](const httplib::Request& req, httplib::Response& res) {
// 		res.set_header("Access-Control-Allow-Origin", "*");
// 		res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
// 		res.set_header(
// 		"Access-Control-Allow-Headers",
// 		"X-Requested-With, Content-Type, Accept, Origin, Authorization");
// 		res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

// 		//获取http请求中peerId和direction的信息
// 		json ret_json = json::parse(req.body);


// 		res.set_content(ret_json.dump(), "application/json");
// 	});

using std::string;
using std::random_device;
using std::default_random_engine;

int HttpServer::message_id = 1;


string HttpServer::strRand(int length) {            // length: 产生字符串的长度
    char tmp;                            // tmp: 暂存一个随机数
    string buffer;                        // buffer: 保存返回值

    // 下面这两行比较重要:
    random_device rd;                    // 产生一个 std::random_device 对象 rd
    default_random_engine random(rd());    // 用 rd 初始化一个随机数发生器 random

    for (int i = 0; i < length; i++) {
        tmp = random() % 36;    // 随机一个小于 36 的整数，0-9、A-Z 共 36 种字符
        if (tmp < 10) {            // 如果随机数小于 10，变换成一个阿拉伯数字的 ASCII
            tmp += '0';
        } else {                // 否则，变换成一个大写字母的 ASCII
            tmp -= 10;
            tmp += 'A';
        }
        buffer += tmp;
    }
    return buffer;
}

uint64_t HttpServer::getTimestampMs() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

int HttpServer::gettimeofday(struct timeval *tp, void *tzp) {
    auto now_stamp = std::chrono::duration_cast<std::chrono::microseconds>(
            std::chrono::system_clock::now().time_since_epoch()).count();
    tp->tv_sec = (decltype(tp->tv_sec)) (now_stamp / 1000000LL);
    tp->tv_usec = now_stamp % 1000000LL;
    return 0;
}


HttpServer::HttpServer(RoomInfo *room) : room_one(room) {
    newPeer();
    newtransport();
    startPublish();
    connectTrasnport();
    sendTrack();
    syncState();
    recvTrack();
    resumeConsumer();
    svr.Options(R"(.*)", [](const httplib::Request &req, httplib::Response &res) {
        res.set_header("Access-Control-Allow-Origin", "*");
        res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
        res.set_header(
                "Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Accept, Origin, Authorization");
        res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");
    });

}


void HttpServer::run() {
    svr.listen("0.0.0.0", 8080);
}

void HttpServer::newPeer() {
    svr.Post("/signaling/join-as-new-peer", [&](const httplib::Request &req, httplib::Response &res) {
        handleRequest(req, res, [&](json &req_json) {
            std::string clientId = strRand(7);
            json res_json;
            res_json["routerRtpCapabilities"] = room_one->routerRtpCapabilities;
            res_json["clientId"] = clientId;
            RoomInfo::RoomState peerState;
            peerState.joinTs = getTimestampMs();
            peerState.lastSeenTs = getTimestampMs();
            room_one->roomStateMap.insert({clientId, peerState});

            res.set_content(res_json.dump(), "application/json");
        });
    });
}

void HttpServer::newtransport() {
    svr.Post("/signaling/create-transport", [&](const httplib::Request &req, httplib::Response &res) {
        res.set_header("Access-Control-Allow-Origin", "*");
        res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
        res.set_header(
                "Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Accept, Origin, Authorization");
        res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

        //获取http请求中peerId和direction的信息
        json req_json = json::parse(req.body);
        std::string clientId = req_json["clientId"].get<std::string>();
        std::string direction = req_json["direction"].get<std::string>();
        //获取当前router
        std::string transportId = strRand(7);

        //存储transportId信息
        if (direction == "recv") {
            room_one->pullTransportMap[clientId] = transportId;
        } else if (direction == "send") {
            room_one->pushTransportMap[clientId] = transportId;
        }


        static std::string cname = "ab05199b";
        room_one->cnameMap[transportId] = cname;
        //创建webrtctransport
        json param_json = {
                {"id",       message_id++},
                {"method",   "router.createWebRtcTransport"},
                {"internal", {
                                     {"RouterId",  room_one->routerId},
                                     {"transportId", transportId}
                             }},
                {"data",     {
                                     {"listenIps", {{{"ip", "127.0.0.1"}}}},
                                     {"enableUdp",   true},
                                     {"enableTcp", true},
                                     {"preferUdp", true},
                                     {"preferTcp", false},
                                     {"initialAvailableOutgoingBitrate", 800000},
                                     {"enableSctp", false},
                                     {"numSctpStreams", {{"OS", 1024}, {"MIS", 1024}}},
                                     {"maxSctpMessageSize", 262144},
                                     {"sctpSendBufferSize", 262144},
                                     {"isDataChannel", true}
                             }
                }
        };

        Channel::Request req_chan(param_json);
        room_one->OnChannelRequest(&req_chan);

        json transportOptions_json;
        transportOptions_json["iceParameters"] = req_chan.jsonResponse["data"]["iceParameters"];
        transportOptions_json["iceCandidates"] = req_chan.jsonResponse["data"]["iceCandidates"];
        transportOptions_json["dtlsParameters"] = req_chan.jsonResponse["data"]["dtlsParameters"];
        transportOptions_json["id"] = req_chan.jsonResponse["data"]["id"];
        json ret_json;
        ret_json["transportOptions"] = transportOptions_json;
        res.set_content(ret_json.dump(), "application/json");
    });
}

void HttpServer::handleRequest(const httplib::Request &req, httplib::Response &res, function<void(json &)> func) {
    res.set_header("Access-Control-Allow-Origin", "*");
    res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
    res.set_header(
            "Access-Control-Allow-Headers",
            "X-Requested-With, Content-Type, Accept, Origin, Authorization");
    res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

    //获取http请求中peerId和direction的信息
    json req_json = json::parse(req.body);
    func(req_json);
}

void HttpServer::startPublish() {
    svr.Post("/signaling/startPublish", [&](const httplib::Request &req, httplib::Response &res) {
        handleRequest(req, res, [&](json &req_json) {
            std::string mediaTag = req_json["mediaTag"].get<std::string>();
            std::string clientId = req_json["clientId"].get<std::string>();
            //获取当前router
            std::string transportId = strRand(7);
            static std::string cname = "ab05199c";
            room_one->cnameMap[transportId] = cname;
            //创建webrtctransport
            json param_json = {
                    {"id",       message_id++},
                    {"method",   "router.createPlainTransport"},
                    {"internal", {
                                         {"RouterId", room_one->routerId},
                                         {"transportId", transportId}
                                 }
                    },
                    {"data",     {
                                         {
                                             "listenIp", {{"ip", "10.8.6.27"}, {"announcedIp", "10.8.6.27"}}
                                         },
                                         {"comedia", false},
                                         {"rtcpMux", false}
                                 }
                    }
            };

            Channel::Request req_chan(param_json);
            room_one->OnChannelRequest(&req_chan);

            room_one->_plainTransportId = transportId;
            /**
             * 建立连接：将解密后的rtp/rtcp发到外部接收端
             */
            param_json = {
                    {"id",       message_id++},
                    {"method",   "transport.connect"},
                    {"internal", {
                                         {"routerId", room_one->routerId},
                                         {"transportId", transportId}
                                 }},
                    {"data",     {
                                         {"ip",       "10.8.6.27"},
                                         {"port",     5006},
                                         {"rtcpPort", 5007}
                                 }
                    }
            };
            Channel::Request conn_req_chan(param_json);
            room_one->OnChannelRequest(&conn_req_chan);

            /**
             * 消费rtp/rtcp数据
             */
            param_json = {
                    {"clientId",       clientId},
                    {"mediaTag", mediaTag},
                    {"transportId", room_one->_plainTransportId}
            };
            const json &consume_ret = consume(param_json);
            /**
             * 消费rtp/rtcp数据
             */
            param_json = {
                    {"clientId",       clientId},
                    {"consumerId", consume_ret["id"]},
                    {"transportId", room_one->_plainTransportId}
            };
            resumeConsumer(param_json);

            json transportOptions_json;
            transportOptions_json["data"] = req_chan.jsonResponse["data"];
            json ret_json;
            ret_json["transportOptions"] = transportOptions_json;
            res.set_content(ret_json.dump(), "application/json");
        });
    });
}

void HttpServer::connectTrasnport() {
    svr.Post("/signaling/connect-transport", [&](const httplib::Request &req, httplib::Response &res) {
        res.set_header("Access-Control-Allow-Origin", "*");
        res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
        res.set_header(
                "Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Accept, Origin, Authorization");
        res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

        //获取http请求中peerId和direction的信息
        json req_json = json::parse(req.body);


        json param_json = {
                {"id",       message_id++},
                {"method",   "transport.connect"},
                {"internal", {
                                     {"routerId",       room_one->routerId},
                                     {"transportId", req_json["transportId"]}
                             }},
                {"data",     {
                                     {"dtlsParameters", req_json["dtlsParameters"]}
                             }
                }
        };

        Channel::Request req_chan(param_json);
        room_one->OnChannelRequest(&req_chan);

        json ret_json;
        ret_json["connected"] = true;
        res.set_content(ret_json.dump(), "application/json");
    });
}

void HttpServer::sendTrack() {
    svr.Post("/signaling/send-track", [&](const httplib::Request &req, httplib::Response &res) {
        res.set_header("Access-Control-Allow-Origin", "*");
        res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
        res.set_header(
                "Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Accept, Origin, Authorization");
        res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

        //获取http请求中peerId和direction的信息
        json req_json = json::parse(req.body);

        if (req_json["kind"] == "video") {
            sendVideoTrack(req, res);
        } else if (req_json["kind"] == "audio") {
            sendAudioTrack(req, res);
        }

    });
}


void HttpServer::sendAudioTrack(const httplib::Request &req, httplib::Response &res) {

    //获取http请求中peerId和direction的信息
    json req_json = json::parse(req.body);

    // std::string cname = req_json["transportId"];
    std::string producerId = strRand(7);

    // std::cout << "send track audio" << std::endl;
    json param_json = {
            {"id",       message_id++},
            {"method",   "transport.produce"},
            {"internal", {
                                 {"routerId", room_one->routerId},
                                 {"transportId", req_json["transportId"]},
                                 {"producerId",    producerId}
                         }},
            {"data",     {
                                 {"kind",     req_json["kind"]},
                                 {"paused",      req_json["paused"]},
                                 {"rtpParameters", req_json["rtpParameters"]},
                                 {"rtpMapping", room_one->audiortpMapping}
                         }
            }
    };
    std::string internal = param_json["internal"].dump();
    room_one->producerIndexMap.push_back(internal);

    room_one->producerMap.insert({req_json["appData"]["mediaTag"], producerId});

    Channel::Request chan_req(param_json);

    room_one->roomState.media["cam-audio"] = {
            {"paused",    req_json["paused"]},
            {"encodings", req_json["rtpParameters"]["encodings"]}
    };


    // auto iter = room_one->roomStateMap.find(room_one->routerId);
    // if(iter != room_one->roomStateMap.end())  {
    // 	auto& roomstate = iter->second;
    // 	roomstate.lastSeenTs = getTimestampMs();
    // 	room_one->roomState.media["cam-audio"] = {
    // 		{"paused",req_json["paused"]},
    // 		{"encodings",req_json["rtpParameters"]["encodings"]}
    // 	};
    // }

    room_one->OnChannelRequest(&chan_req);

    observerAddProducer(producerId);

    json ret_json;
    ret_json["id"] = producerId;
    res.set_content(ret_json.dump(), "application/json");

}

void HttpServer::sendVideoTrack(const httplib::Request &req, httplib::Response &res) {

    //获取http请求中peerId和direction的信息
    json req_json = json::parse(req.body);

    std::string producerId = strRand(7);

    // std::cout << "send track video" << std::endl;
    json param_json = {
            {"id",       message_id++},
            {"method",   "transport.produce"},
            {"internal", {
                                 {"routerId", room_one->routerId},
                                 {"transportId", req_json["transportId"]},
                                 {"producerId",    producerId}
                         }},
            {"data",     {
                                 {"kind",     req_json["kind"]},
                                 {"paused",      req_json["paused"]},
                                 {"rtpParameters", req_json["rtpParameters"]},
                                 {"rtpMapping", room_one->rtpMapping}
                         }
            }
    };
    std::string internal = param_json["internal"].dump();
    room_one->producerIndexMap.push_back(internal);

    room_one->producerMap.insert({req_json["appData"]["mediaTag"], producerId});

    Channel::Request chan_req(param_json);

    room_one->roomState.media["cam-video"] = {
            {"paused",    req_json["paused"]},
            {"encodings", req_json["rtpParameters"]["encodings"]}
    };

    // auto iter = room_one->roomStateMap.find(room_one->routerId);
    // if(iter != room_one->roomStateMap.end())  {
    // 	auto& roomstate = iter->second;
    // 	roomstate.lastSeenTs = getTimestampMs();
    // 	roomstate.media["cam-video"] = {
    // 		{"paused",req_json["paused"]},
    // 		{"encodings",req_json["rtpParameters"]["encodings"]}
    // 	};
    // }
    room_one->OnChannelRequest(&chan_req);
    json ret_json;
    ret_json["id"] = producerId;
    res.set_content(ret_json.dump(), "application/json");

}


void HttpServer::recvTrack() {
    svr.Post("/signaling/recv-track", [&](const httplib::Request &req, httplib::Response &res) {
        res.set_header("Access-Control-Allow-Origin", "*");
        res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
        res.set_header(
                "Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Accept, Origin, Authorization");
        res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");
        json req_json = json::parse(req.body);
        json json = consume(req_json);
        res.set_content(json.dump(), "application/json");
    });
}

json HttpServer::consume(const json &req_json) {

    std::string peerId = req_json["clientId"].get<std::string>();
    std::string mediaTag = req_json["mediaTag"].get<std::string>();
    std::string consumerId = strRand(7);


    this->room_one->consumerMap[consumerId] = this->room_one->producerMap[mediaTag];
    json param_json;
    param_json["id"] = HttpServer::message_id++;
    param_json["internal"]["routerId"] = this->room_one->routerId;
    if (req_json.contains("transportId")) {
        param_json["internal"]["transportId"] = req_json["transportId"].get<string>();
    } else {
        param_json["internal"]["transportId"] = this->room_one->pullTransportMap[peerId];
    }
    param_json["internal"]["consumerId"] = consumerId;
    param_json["internal"]["producerId"] = this->room_one->producerMap[mediaTag];
    param_json["method"] = "transport.consume";
    param_json["data"]["rtpParameters"] = this->room_one->rtpParameters;
    param_json["data"]["kind"] = "video";
    param_json["data"]["type"] = "simple";
    param_json["data"]["paused"] = true;
    param_json["data"]["consumableRtpEncodings"] = this->room_one->consumableRtpParameters["encodings"];
    std::string internal = param_json["internal"].dump();
    this->room_one->consumerIndexMap.push_back(internal);

    Channel::Request req_chan(param_json);
    this->room_one->OnChannelRequest(&req_chan);

    json ret_json;
    ret_json["id"] = consumerId;
    ret_json["kind"] = "video";
    ret_json["producerId"] = this->room_one->producerMap[mediaTag];
    ret_json["producerPaused"] = false;
    ret_json["rtpParameters"] = this->room_one->rtpParameters;
    ret_json["type"] = "simple";
    return ret_json;
}

void HttpServer::resumeConsumer() {
    svr.Post("/signaling/resume-consumer", [&](const httplib::Request &req, httplib::Response &res) {
        res.set_header("Access-Control-Allow-Origin", "*");
        res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
        res.set_header(
                "Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Accept, Origin, Authorization");
        res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");
        json req_json = json::parse(req.body);
        const json &json = resumeConsumer(req_json);
        res.set_content(json.dump(), "application/json");
    });
}

json HttpServer::resumeConsumer(const json &req_json) {
    string peerId = req_json["clientId"].get<string>();
    string consumerId = req_json["consumerId"].get<string>();

    json param_json;
    param_json["id"] = message_id++;
    param_json["internal"]["routerId"] = room_one->routerId;
    if (req_json.contains("transportId")) {
        param_json["internal"]["transportId"] = req_json["transportId"].get<string>();
    } else {
        param_json["internal"]["transportId"] = room_one->pullTransportMap[peerId];
    }
    param_json["internal"]["consumerId"] = consumerId;
    param_json["internal"]["producerId"] = room_one->consumerMap[consumerId];
    param_json["method"] = "consumer.resume";

    string internal = param_json["internal"].dump();
    room_one->consumerIndexMap.push_back(internal);


    Channel::Request req_chan(param_json);
    room_one->OnChannelRequest(&req_chan);


    json ret_json;
    ret_json["resumed"] = true;
    return ret_json;
}

void HttpServer::observerAddProducer(std::string producerId) {
    json req_json;
    req_json["id"] = HttpServer::message_id++;
    req_json["internal"]["routerId"] = room_one->routerId;
    req_json["internal"]["rtpObserverId"] = room_one->audioObserverId;
    req_json["internal"]["producerId"] = producerId;
    req_json["method"] = "rtpObserver.addProducer";
    Channel::Request req(req_json);
    room_one->OnChannelRequest(&req);
}


void HttpServer::syncState() {
    svr.Post("/signaling/sync", [&](const httplib::Request &req, httplib::Response &res) {
        res.set_header("Access-Control-Allow-Origin", "*");
        res.set_header("Allow", "GET, POST, HEAD, OPTIONS");
        res.set_header(
                "Access-Control-Allow-Headers",
                "X-Requested-With, Content-Type, Accept, Origin, Authorization");
        res.set_header("Access-Control-Allow-Methods", "OPTIONS, GET, POST, HEAD");

        getState();

        json req_json = json::parse(req.body);
        auto peerId = req_json["peerId"];
        // std::cout << peerId << std::endl;

        // auto roomstate = room_one->roomStateMap.find(peerId);

        json res_json = {{"peers",
                                 {{peerId,
                                                 {{"joinTs", 1609839411555},
                                                         {"lastSeenTs", getTimestampMs()},
                                                         {"media", room_one->roomState.media},
                                                         {"consumerLayers", {}},
                                                         {"stats", room_one->roomState.stats}}}}},
                         {"activeSpeaker",
                                 {{"producerId", nullptr}, {"volume", nullptr}, {"peerId", nullptr}}}};
        res.set_content(res_json.dump(), "application/json");
    });
}


void HttpServer::getState() {

    for (auto internal : room_one->producerIndexMap) {
        json state_array;
        json internal_json = json::parse(internal);
        json param_json = {
                {"id",       HttpServer::message_id++},
                {"method",   "producer.getStats"},
                {"internal", internal_json}
        };
        Channel::Request chan_req(param_json);
        room_one->OnChannelRequest(&chan_req);
        for (auto element : chan_req.jsonResponse["data"]) {
            if (element["kind"] != "video") {
                continue;
            }
            json state_json;
            state_json["bitrate"] = element["bitrate"];
            state_json["fractionLost"] = element["fractionLost"];
            state_json["jitter"] = element["jitter"];
            state_json["score"] = element["score"];
            state_json["rid"] = element["rid"];
            state_array.push_back(state_json);
        }
        std::string producerId = internal_json["producerId"].get<std::string>();
        room_one->roomState.stats[producerId] = state_array;
    }

    for (auto internal : room_one->consumerIndexMap) {
        json state_array;
        json internal_json = json::parse(internal);
        json param_json = {
                {"id",       HttpServer::message_id++},
                {"method",   "consumer.getStats"},
                {"internal", internal_json}
        };
        Channel::Request chan_req(param_json);
        room_one->OnChannelRequest(&chan_req);
        for (auto element : chan_req.jsonResponse["data"]) {
            if (element["type"] != "outbound-rtp") {
                continue;
            }
            json state_json;
            state_json["bitrate"] = element["bitrate"];
            state_json["fractionLost"] = element["fractionLost"];
            state_json["score"] = element["score"];
            state_array.push_back(state_json);
        }
        std::string consumerId = internal_json["consumerId"].get<std::string>();
        room_one->roomState.stats[consumerId] = state_array;
    }

}




